/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

import { expect } from '@jest/globals';
import { Json5Reader } from '@ohos/hvigor';
import * as fs from 'fs';
import { copyFile } from 'fs-extra';
import * as path from 'path';
import { resolve } from 'path';

import { CompileModeEnum } from '../../../src/enum/compile-mode-enum';
import { ArtifactType } from '../../../src/tasks/har/har-extend-info';
import { appRoot, initByStageEmptyApp, initExecuteCommand, runHvigor, runShouldFail } from '../common/common';
import { ModuleBuildProfileModifier } from '../common/modifier/module-build-profile-modifier';
import { ModuleJsonModifier } from '../common/modifier/module-json-modifier';
import { PackageJsonModifier } from '../common/modifier/package-json-modifier';

describe('Test package obfuscate har && hap depends obfuscate har module.', () => {
  test.concurrent('test stage module obfuscate har module package', async () => {
    await initByStageEmptyApp({
      cb: async (dir) => {
        const libraryModuleBuildProfileModifier = new ModuleBuildProfileModifier(dir, 'library');
        libraryModuleBuildProfileModifier.setArtifactType(ArtifactType.OBFUSCATION).outputSync();

        const libraryPackageJsonModifier = new PackageJsonModifier(dir, 'library');
        libraryPackageJsonModifier.updateMainField('').outputSync();

        const commands = initExecuteCommand({
          module: 'library',
          tasks: 'assembleHar',
        });
        const result = runHvigor(dir, commands);

        // run successful
        expect(result.stdout.includes('BUILD SUCCESSFUL')).toBeTruthy();
        const buildCachePackageHarPath = path.resolve(dir, 'library/build/default/cache/default/default@PackageHar');
        const buildCachePackageJsonPath = path.resolve(buildCachePackageHarPath, 'oh-package.json5');
        const generatePackPackageJson = Json5Reader.getJson5Obj(buildCachePackageJsonPath);
        expect(generatePackPackageJson.artifactType).toBe(ArtifactType.OBFUSCATION);
        expect(generatePackPackageJson.main).toBe('index.js');
        expect(generatePackPackageJson.types).toBe('index.d.ets');

        const executeAgainResult = runHvigor(dir, commands);

        // run successful
        expect(executeAgainResult.stdout.includes('BUILD SUCCESSFUL')).toBeTruthy();
        expect(executeAgainResult.stdout.includes('UP-TO-DATE :library:default@HarCompileArkTS...'));
        expect(executeAgainResult.stdout.includes('UP-TO-DATE :library:default@PackageHar...'));

        libraryPackageJsonModifier.updateMainField('src/main/ets/components/MainPage/MainPage.ets').outputSync();
        const executeAgainAfterChangeMainFieldResult = runHvigor(dir, commands);

        // run successful
        expect(executeAgainAfterChangeMainFieldResult.stdout.includes('BUILD SUCCESSFUL')).toBeTruthy();
        expect(executeAgainAfterChangeMainFieldResult.stdout
          .includes('Finished :library:default@HarCompileArkTS...')).toBeTruthy();

        // clear jest module cache
        jest.resetModules();
        const generatePackPackageJson2 = Json5Reader.getJson5Obj(buildCachePackageJsonPath);
        expect(generatePackPackageJson2.artifactType).toBe(ArtifactType.OBFUSCATION);
        expect(generatePackPackageJson2.main).toBe('src/main/ets/components/MainPage/MainPage.js');
        expect(generatePackPackageJson2.types).toBe('src/main/ets/components/MainPage/MainPage.d.ets');
      },
    });
  });

  test.concurrent('test Stage module depends obfuscate har package hap', async () => {
    await initByStageEmptyApp({
      prepare: async (dir) => {
        const entryPackageJson = new PackageJsonModifier(dir, 'entry');
        await copyFile(
          resolve(appRoot(), 'test/resources/extra/har-packages/stage_ets_native_obfuscate.har'),
          resolve(dir, 'stage_ets_native_obfuscate.har'));

        // 依赖本地闭源har和依赖本地闭源har har包
        entryPackageJson.updateDependency('library', 'file:../library')
          .updateDependency('stage_ets_native_obfuscate', 'file:../stage_ets_native_obfuscate.har');

        // copy extra resources
        entryPackageJson.outputSync();

        const entryPageIndexEtsFilePath = path.resolve(dir, 'entry/src/main/ets/pages/index.ets');

        // 修改entry源码,依赖library中的接口
        fs.writeFileSync(entryPageIndexEtsFilePath, 'import hilog from \'@ohos.hilog\';\n' +
          'import testNapi from \'libentry.so\'\n' +
          'import { MainPage } from \'library\'\n' +
          'import { testObfuscate } from \'stage_ets_native_obfuscate\'\n' +
          'console.log(testObfuscate())\n' +
          '\n' +
          '@Entry\n' +
          '@Component\n' +
          'struct Index {\n' +
          '  @State message: string = \'Hello World\'\n' +
          '\n' +
          '  build() {\n' +
          '    Row() {\n' +
          '      Column() {\n' +
          '        Text(this.message)\n' +
          '          .fontSize(50)\n' +
          '          .fontWeight(FontWeight.Bold)\n' +
          '          .onClick(() => {\n' +
          '            hilog.info(0x0000, \'testTag\', \'Test NAPI 2 + 3 = %{public}d\', testNapi.add(2, 3));\n' +
          '            MainPage()\n' +
          '          })\n' +
          '      }\n' +
          '      .width(\'100%\')\n' +
          '    }\n' +
          '    .height(\'100%\')\n' +
          '  }\n' +
          '}\n');
      },
      cb: async (dir) => {
        const libraryModuleModifier = new ModuleBuildProfileModifier(dir, 'library');

        // 修改本地的har为闭源har
        libraryModuleModifier.setArtifactType(ArtifactType.OBFUSCATION).outputSync();

        const commands = initExecuteCommand({
          mode: 'module',
          product: 'default',
          module: 'entry@default',
          tasks: 'assembleHap',
        });
        const resultWithJsBundle = runHvigor(dir, commands);

        // run successful with compile mode jsBundle
        expect(resultWithJsBundle.stdout.includes('BUILD SUCCESSFUL')).toBeTruthy();

        const entryModuleModifier = new ModuleBuildProfileModifier(dir, 'entry');

        // 修改entry的编译模式为esModule
        entryModuleModifier.setCompileMode(CompileModeEnum.ES_MODULE).outputSync();

        const resultWithEsModule = runHvigor(dir, commands);

        // run successful with compile mode jsBundle
        expect(resultWithEsModule.stdout.includes('BUILD SUCCESSFUL')).toBeTruthy();
      },
      runNpmInstall: true,
    });
  });

  test.concurrent('test hap compatibleVersion large harVersion', async () => {
    await initByStageEmptyApp({
      prepare: async (dir) => {
        const entryPackageJson = new PackageJsonModifier(dir, 'entry');
        await copyFile(
          resolve(appRoot(), 'test/resources/extra/har-packages/stage_ets_native_obfuscate.har'),
          resolve(dir, 'stage_ets_native_obfuscate.har'));

        // 依赖本地闭源har和依赖本地闭源har包
        entryPackageJson
          .updateDependency('stage_ets_native_obfuscate', 'file:../stage_ets_native_obfuscate.har');

        // copy extra resources
        entryPackageJson.outputSync();
      },
      cb: async (dir) => {
        const libraryModuleModifier = new ModuleBuildProfileModifier(dir, 'library');

        // 修改本地的har为闭源har
        libraryModuleModifier.setArtifactType(ArtifactType.OBFUSCATION).outputSync();
        const harModuleJsonPath = path.resolve(
          dir, 'entry/oh_modules/stage_ets_native_obfuscate/src/main/module.json');
        const jsonObj = require(harModuleJsonPath);

        fs.writeFileSync(harModuleJsonPath, JSON.stringify({
          ...jsonObj,
          'app': {
            'bundleName': 'com.example.myapplication',
            'debug': true,
            'versionCode': 1000000,
            'versionName': '1.0.0',
            'minAPIVersion': 10,
            'targetAPIVersion': 9,
            'apiReleaseType': 'Beta4'
          },
        }));

        const commands = initExecuteCommand({
          mode: 'module',
          product: 'default',
          module: 'entry@default',
          tasks: 'assembleHap',
        });

        // run successful with compile mode jsBundle
        runShouldFail(dir, commands,
          'The compatibleSdkVersion 9 cannot be smaller than version 10 declared in library');
      },
      runNpmInstall: true
    });
  });

  test.concurrent('test Stage module depends obfuscate har module.json5 merge', async () => {
    await initByStageEmptyApp({
      prepare: async (dir) => {
        const entryPackageJson = new PackageJsonModifier(dir, 'entry');

        // 依赖本地library和依赖本地library1
        entryPackageJson.updateDependency('library', 'file:../library')
          .updateDependency('library1', 'file:../library1').outputSync();
      },
      cb: async (dir) => {
        const libraryModuleModifier = new ModuleBuildProfileModifier(dir, 'library');

        // 修改本地的library为闭源har
        libraryModuleModifier.setArtifactType(ArtifactType.OBFUSCATION).outputSync();

        const entryModuleJsonModifier = new ModuleJsonModifier(dir, 'entry');
        entryModuleJsonModifier.setMetaData([
          {
            'name': 'string',
            'value': 'string',
          },
          {
            'name': 'hap_string',
            'value': 'hap_string',
          },
          {
            'value': 'string',
          }
        ]).setRequestPermissions([
          {
            'name': 'ohos.permission.WRITE',
          },
          {
            'name': 'ohos.permission.PROVIDER',
          }
        ]).outputSync();

        const libraryModuleJsonModifier = new ModuleJsonModifier(dir, 'library');
        libraryModuleJsonModifier.setMetaData([
          {
            'name': 'string',
            'value': 'har_string',
          },
          {
            'name': 'har_string',
            'value': 'har_string',
          },
        ]).setRequestPermissions([
          {
            'name': 'ohos.permission.WRITE',
          },
          {
            'name': 'ohos.permission.PROVIDER1',
          }
        ]).outputSync();

        const library1ModuleJsonModifier = new ModuleJsonModifier(dir, 'library1');
        library1ModuleJsonModifier.setMetaData([
          {
            'name': 'har_string',
            'value': 'har1_string',
          },
          {
            'name': 'har1_string',
            'value': 'har1_string',
          },
        ]).setRequestPermissions([
          {
            'name': 'ohos.permission.PROVIDER1',
          },
          {
            'name': 'ohos.permission.PROVIDER2',
          }
        ]).outputSync();

        const commands = initExecuteCommand({
          mode: 'module',
          product: 'default',
          module: 'entry@default',
          tasks: 'assembleHap',
        });
        const resultWithJsBundle = runHvigor(dir, commands);

        // run successful with compile mode jsBundle
        expect(resultWithJsBundle.stdout.includes('BUILD SUCCESSFUL')).toBeTruthy();

        // check merged module.json
        const moduleJsonPath = path
          .resolve(dir, 'entry/build/default/intermediates/process_profile/default/module.json');
        const moduleJsonObj = require(moduleJsonPath);
        expect(moduleJsonObj.module.metadata).toStrictEqual([
          {
            'name': 'string',
            'value': 'string'
          },
          {
            'name': 'hap_string',
            'value': 'hap_string'
          },
          {
            'value': 'string'
          },
          {
            'name': 'har_string',
            'value': 'har_string'
          },
          {
            'name': 'har1_string',
            'value': 'har1_string'
          }
        ]);
        expect(moduleJsonObj.module.requestPermissions).toStrictEqual([
          { name: 'ohos.permission.WRITE' },
          { name: 'ohos.permission.PROVIDER' },
          { name: 'ohos.permission.PROVIDER1' },
          { name: 'ohos.permission.PROVIDER2' }
        ]);
      },
      runNpmInstall: true,
    });
  });

  test.concurrent('test package har with special files', async () => {
    await initByStageEmptyApp({
      cb: async (dir) => {
        const libraryModuleBuildProfileModifier = new ModuleBuildProfileModifier(dir, 'library');
        libraryModuleBuildProfileModifier.setArtifactType(ArtifactType.OBFUSCATION).outputSync();

        const libraryPackageJsonModifier = new PackageJsonModifier(dir, 'library');
        libraryPackageJsonModifier.updateMainField('').outputSync();

        const specialFile: string[] = ['changelog.md', 'README.md', 'readme.OpenSource', 'LICENSE'];

        specialFile.forEach(fileName => {
          const filePath = resolve(dir, 'library', fileName);
          fs.writeFileSync(filePath, '');
        });

        const commands = initExecuteCommand({
          module: 'library',
          tasks: 'assembleHar',
        });
        const result = runHvigor(dir, commands);
        expect(result.stdout.includes('Finished :library:default@PackageHar')).toBeTruthy();
        expect(result.stdout.includes('BUILD SUCCESSFUL')).toBeTruthy();

        const output = path.resolve(dir, 'library/build/default/outputs/default');
        expect(fs.readdirSync(output).indexOf('library.har')).not.toEqual(-1);

        specialFile.forEach(fileName => {
          const filePath = resolve(dir, 'library/build/default/cache/default/default@PackageHar');
          expect(fs.readdirSync(filePath).indexOf(fileName)).not.toEqual(-1);
        });
      },
    });
  });
});
